local Player = require 'packages.mahjong.riichi_mahjong.core.player'
local AI = require "packages.mahjong.riichi_mahjong.server.ai"
local Engine = require "packages.mahjong.riichi_mahjong.core.engine"
local Meld = require "packages.mahjong.riichi_mahjong.core.meld"
local MJ = require "packages.mahjong.utility.mj_utility"

---@class RiichiMahjong.ServerPlayer : RiichiMahjong.Player, ServerPlayerBase
local ServerPlayer = Player:subclass("RiichiMahjong.ServerPlayer")
ServerPlayer:include(Fk.Base.ServerPlayerBase)

---@class RiichiMahjong.ServerPlayer
---@field public room RiichiMahjong.Room
---@field public ai RiichiMahjong.AI
---@field public player_tiles integer[] @ 本人拥有的麻将牌

function ServerPlayer:initialize(_self)
  Player.initialize(self)
  Fk.Base.ServerPlayerBase.initialize(self, _self)

  self.ai = AI:new(self)
  self.comming = 0
end

---获得不副露的手牌
---@return integer[]
function ServerPlayer:getConcealedHand()
  return table.filter(self.player_tiles, function (tile)
    return not table.find(self.melds, function (meld)
      return table.contains(meld.tiles, tile)
    end)
  end)
end

---@return integer[] @ 能立直的牌
function ServerPlayer:getRiichi()
  if true then
    return {}
  end
  --TODO: 暂时先不开立直
  local tiles = table.map(self.player_tiles, Engine.getTileById)
  MJ.SortCards(tiles)
  local result = {}

  for i = 1,#tiles do
    local getTile = function (index)
      if index < i then
        return tiles[index]
      else
        return tiles[index + 1]
      end
    end
    
    -- 检查是否有七对子
    local no_pair = 0
    for j = 1, #tiles do
      if getTile(j).number ~= getTile(j+1).number then
        no_pair = no_pair + 1
      end
      j = j+1
    end
    if no_pair == 1 then
      table.insert(result, tiles[i].id)
      goto continue
    end
    -- 检查国士无双
    local requiredTiles = {
      {suit = "m", number = 1},
      {suit = "m", number = 9},
      {suit = "p", number = 1},
      {suit = "p", number = 9},
      {suit = "s", number = 1},
      {suit = "s", number = 9},
      {suit = "z", number = 1},
      {suit = "z", number = 2},
      {suit = "z", number = 3},
      {suit = "z", number = 4},
      {suit = "z", number = 5},
      {suit = "z", number = 6},
      {suit = "z", number = 7}
    }
    local canRong = 2
    for _, tileRequire in ipairs(requiredTiles) do
      for j = 1, #tiles-1 do
        if getTile(j).suit == tileRequire.suit and getTile(j).number == tileRequire.number then
          canRong = canRong - 1
          break
        end
      end
    end
    for j = 1, #tiles-1 do
      if canRong <= 0 or not table.find(requiredTiles, function (tileRequire)
        return getTile(j).suit == tileRequire.suit and getTile(j).number == tileRequire.number
      end) then
        canRong = 0
        break
      end
    end
    if canRong > 0 then
      table.insert(result, tiles[i].id)
      goto continue
    end
    
    -- 检查标准型（递归分解顺子刻子）
    local tempCards = {}
    for j = 1,13 do
      table.insert(tempCards, getTile(j))
    end
    
    local function decompose(cards, hasPair)
      table.sort(cards, function(a,b)
        if a.suit ~= b.suit then return a.suit < b.suit end
        return a.number < b.number
      end)
      
      if #cards == 0 then return hasPair end
      
      -- 尝试找刻子
      if #cards >=3 and cards[1]:sameAs(cards[2]) and cards[1]:sameAs(cards[3]) then
        return decompose(table.slice(cards, 4), hasPair)
      end
      
      -- 尝试找顺子（非字牌）
      if cards[1].suit ~= "z" then
        local seq = {cards[1]}
        for j = 2, #cards do
          if #seq <3 and cards[j].suit == seq[1].suit 
            and cards[j].number == seq[#seq].number +1 then
            table.insert(seq, cards[j])
          end
        end
        if #seq >=3 then
          local remaining = not table.contains(cards, seq)
          return decompose(remaining, hasPair)
        end
      end
      
      -- 尝试找对子
      if not hasPair and #cards >=2 and cards[1]:sameAs(cards[2]) then
        return decompose(table.slice(cards,3), true)
      end
      
      return false
    end
    
    if decompose(tempCards, false) then
      table.insert(result, tiles[i].id)
    end
    ::continue::
  end
  return result
end

---@return integer[]
function ServerPlayer:getKongs()
  local result = {}
  local processed = {}
  local tiles = {}
  
  -- 先创建一个牌对象的表，避免重复调用getTileById
  for _, t in ipairs(self.player_tiles) do
    local tile = Engine:getTileById(t)
    if tile then
      tiles[t] = tile
    end
  end
  
  -- 遍历每个牌，寻找四张相同的牌
  for id, tile in pairs(tiles) do
    -- 跳过已经处理过的牌
    if not processed[id] then
      local sameTiles = {id}
      
      -- 寻找与当前牌相同的其他牌
      for otherId, otherTile in pairs(tiles) do
        if id ~= otherId and not processed[otherId] and tile:sameAs(otherTile) then
          table.insert(sameTiles, otherId)
        end
      end
      
      -- 如果有四张相同的牌，则添加到结果中
      if #sameTiles >= 4 then
        -- 只取前四张相同的牌
        for i = 1, 4 do
          table.insert(result, sameTiles[i])
          processed[sameTiles[i]] = true
        end
      end
    end
  end
  
  return result
end

--- 获得能吃碰杠的牌组
---@param tile RiichiMahjong.Tile
---@param judgeChow boolean? @ 是否判断吃
function ServerPlayer:getCPK(tile, judgeChow)
  local result = {[Meld.Type.Chow] = {}, [Meld.Type.Pong] = {}, [Meld.Type.Kong] = {}}
  local Engine = require "packages.mahjong.riichi_mahjong.core.engine"
  local handcards = table.map(self:getConcealedHand(), function (id)
    return Engine:getTileById(id)
  end)
  MJ.SortCards(handcards)
  local sameCard = table.filter(handcards, function (t)
    return t:sameAs(tile)
  end)
  -- 找杠
  if #sameCard >= 3 then
    table.insert(result[Meld.Type.Kong], {sameCard[1].id, sameCard[2].id, sameCard[3].id})
  end
  -- 找碰
  if #sameCard >= 2 then
    --非宝牌
    if #sameCard == 2 or tile.suit == "z" or tile.number ~= 5 then
      table.insert(result[Meld.Type.Pong], {sameCard[1].id, sameCard[2].id})
    else
      -- 有宝牌时可能有多重组合
      local dora = table.find(sameCard, function (t, index)
        if t.number == tile.number and t.suit == tile.suit and t.dora == true then
          return true
        end
      end)
      local notDora = table.filter(sameCard, function (t)
        return not t.dora
      end)
      if dora then
        table.insert(result[Meld.Type.Pong], {notDora[1].id, notDora[2].id})
        table.insert(result[Meld.Type.Pong], {notDora[1].id, dora.id})
      else
        table.insert(result[Meld.Type.Pong], {sameCard[1].id, sameCard[2].id})
      end
    end
  end

  if judgeChow then
    -- 找吃
    local chowTypes = {
      {tile.number - 2, tile.number - 1},
      {tile.number - 1, tile.number + 1},
      {tile.number + 1, tile.number + 2},
    }
    for _, type in ipairs(chowTypes) do
      local chow_first = table.filter(handcards, function (t)
        return t.number == type[1] and t.suit == tile.suit
      end)
      local chow_second = table.filter(handcards, function (t)
        return t.number == type[2] and t.suit == tile.suit
      end)
      if #chow_first > 0 and #chow_second > 0 then
        -- 有宝牌时可能有多重组合
        local dora_first = table.filter(chow_first, function (t)
          return t.dora
        end)
        local not_dora_first = table.filter(chow_first, function (t)
          return not t.dora
        end)
        local dora_second = table.filter(chow_second, function (t)
          return t.dora
        end)
        local not_dora_second = table.filter(chow_second, function (t)
          return not t.dora
        end)
        
        -- 添加不同的组合
        -- 宝牌+普通牌组合
        for _, dora1 in ipairs(dora_first) do
          for _, not_dora2 in ipairs(not_dora_second) do
            table.insert(result[Meld.Type.Chow], {dora1.id, not_dora2.id})
          end
        end
        for _, not_dora1 in ipairs(not_dora_first) do
          for _, dora2 in ipairs(dora_second) do
            table.insert(result[Meld.Type.Chow], {not_dora1.id, dora2.id})
          end
        end
        -- 普通牌+普通牌组合
        for _, not_dora1 in ipairs(not_dora_first) do
          for _, not_dora2 in ipairs(not_dora_second) do
            table.insert(result[Meld.Type.Chow], {not_dora1.id, not_dora2.id})
          end
        end
      end
    end
  end
  return result
end

function ServerPlayer:getRyukyoku()
  local room = self.room
  local orphans = {} ---@type RiichiMahjong.Tile[]
  for _, tid in ipairs(self.player_tiles) do
    local tile = Engine:getTileById(tid)
    if tile.number == 1 or tile.number == 9 or tile.suit == "z" then
      if not table.find(orphans, function(e) return e:sameAs(tile) end) then
        table.insert(orphans, tile)
      end
    end
  end
  return #orphans >= 9
end

return ServerPlayer

